﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using BLToolkit.Data;
using BLToolkit.DataAccess;
using BLToolkit.Reflection.Emit;
using BLToolkit.TypeBuilder;
using BLToolkit.TypeBuilder.Builders;

namespace Devsoft.DB.Models.BltookitEx
{
    public class ManagerBuilder : DataAccessorBuilder
    {
        /*
        public ManagerBuilder()
        {
            var fi = this.GetType().BaseType.GetField("_baseType", BindingFlags.Instance | BindingFlags.NonPublic);
            fi.SetValue(this, typeof(Manager<>));
        }
         * */
        public override bool IsApplied(BLToolkit.TypeBuilder.Builders.BuildContext context, BLToolkit.TypeBuilder.Builders.AbstractTypeBuilderList builders)
        {
            if (context.IsBeforeStep)
            {
                return true;
            }
            if (context.IsBuildStep)
            {
                if (context.IsAbstractMethod)
                {
                    return true;
                }
                if (context.CurrentMethod != null && context.CurrentMethod.Name.Contains("DeleteData"))
                {
                    
                }
            }
            return base.IsApplied(context, builders);
        }

        protected override void CallSetCommand()
        {
            base.CallSetCommand();
            var commandTimeOutAttr =Context.CurrentMethod.GetCustomAttributes(typeof (CommandTimeOutAttribute), true).FirstOrDefault();
            if (commandTimeOutAttr != null)
            {
                EmitHelper emit = Context.MethodBuilder.Emitter;
                emit
                    .ldloc(_locManager)
                    .callvirt(GetMemberInfo((DbManager db) => db.Command).GetGetMethod())
                    .ldc_i4((commandTimeOutAttr as CommandTimeOutAttribute).TimeOut)
                    .callvirt(GetMemberInfo((System.Data.IDbCommand cmd) => cmd.CommandTimeout).GetSetMethod());    
            }
            

        }

        public static PropertyInfo GetMemberInfo<T, U>(Expression<Func<T, U>> expression)
        {
            var member = expression.Body as MemberExpression;
            if (member != null)
            {
                PropertyInfo propInfo = member.Member as PropertyInfo;
                if (propInfo == null)
                    throw new ArgumentException(string.Format(
                        "Expression '{0}' refers to a field, not a property.",expression.ToString()));
                Type type = typeof(T);
                if (type != propInfo.ReflectedType &&
                    !type.IsSubclassOf(propInfo.ReflectedType))
                    throw new ArgumentException(string.Format(
                        "Expresion '{0}' refers to a property that is not from type {1}.",
                        expression.ToString(),
                        type));

                return propInfo;
            }

            throw new ArgumentException("Expression is not a member access", "expression");
        }

        protected override void BeforeBuildType()
        {
            base.BeginMethodBuild();

            if (Context.Type.Type.UnderlyingSystemType.GetConstructor(new Type[] { typeof(DbManager) }) != null)
            {
                var con = this.Context.TypeBuilder.DefinePublicConstructor(typeof(DbManager));
                con.Emitter
                .ldarg_0
                .ldarg_1
                .call(Context.Type.Type.UnderlyingSystemType.GetConstructor(new Type[] { typeof(DbManager) }))
                .ret()
                ;
            }
        }
    }
}
